<?php

namespace App\Http\Controllers;

use App\Enums\OrderState;
use App\Models\Order;
use App\Models\User;
use App\Rules\UrlSignature;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpClient\Exception\ClientException;

class PaymentController extends Controller
{
    protected ?Order $order = null;

    protected ?User $user = null;

    public function wechat(Request $request)
    {
        try {
            $this->checkWechatAgent($request);
            $this->initApplication($request);
            $this->checkRequestParams($request);
            $this->checkOrder($request);
            $this->checkUser($request);
        } catch (Exception $e) {
            return $this->showError($e->getMessage());
        }
        try {
            $jsConfig = $this->buildPaymentConfig();

            return view('payment.wechat', [
                'application' => $this->application,
                'callback' => $request->callback,
                'jsConfig' => $jsConfig,
            ]);
        } catch (Exception $exception) {
            if ($exception instanceof ClientException && $exception->getCode() == 400) {
                return $this->showError('创建订单失败，请检查参数');
            }

            return $this->showError($exception->getMessage());
        }
    }

    /**
     * @throws Exception
     */
    protected function checkRequestParams(Request $request): void
    {
        $validator = Validator::make($request->all(), [
            'no' => 'required|bail|string|exists:App\Models\Order,no',
            'app_key' => 'required|bail|exists:App\Models\Application,app_key',
            'signature' => [
                'required',
                'bail',
                'string',
                new UrlSignature,
            ],
        ], [
            'no.required' => '订单编号必须存在',
            'no.string' => '订单编号格式不正确',
            'no.exists' => '订单编号不存在',
            'app_key.required' => '应用标识不能为空',
            'app_key.exists' => '应用标识不存在',
            'signature.required' => '签名错误',
            'signature.string' => '签名错误',
        ]);

        if ($validator->fails()) {
            throw new Exception($validator->errors()->first());
        }
    }

    /**
     * @throws Exception
     */
    protected function checkOrder(Request $request)
    {
        $no = $request->no;

        $order = Order::where('no', $no)->first();
        if ($order->application->isNot($this->application)) {
            throw new Exception('非法操作');
        }

        if ($order->state != OrderState::INIT) {
            throw new Exception('请勿重复支付');
        }

        $this->order = $order;
    }

    /**
     * @throws Exception
     */
    protected function checkUser(Request $request)
    {
        $user = User::find($this->order->user_id);
        if ($user->lock) {
            throw new Exception('用户存在风险，暂时无法支付');
        }

        $this->user = $user;
    }

    /**
     * @throws Exception
     */
    protected function buildPaymentConfig(): string
    {
        $payment = $this->order->wechat->payment->init();

        $params = [
            'mchid' => $this->order->wechat->payment->mch_id,
            'out_trade_no' => $this->order->no,
            'appid' => $this->order->wechat->appid,
            'description' => $this->user->openid,
            'notify_url' => route('api.notify.payment', [
                'appKey' => $this->application->app_key,
            ]),
            'amount' => [
                'total' => intval($this->order->amount * 100),
                'currency' => 'CNY',
            ],
            'payer' => [
                'openid' => $this->user->openid,
            ],
        ];

        $response = $payment->getClient()
            ->postJson('v3/pay/transactions/jsapi', $params);

        if ($response->isSuccessful()) {
            $utils = $payment->getUtils();
            $config = $utils->buildBridgeConfig($response->toArray()['prepay_id'], $this->order->wechat->appid);

            return json_encode($config);
        } else {
            throw new Exception('订单创建失败');
        }
    }
}
